package com.lagou.rpc.provider.handler;

import com.alibaba.fastjson.JSON;
import com.lagou.rpc.common.RpcRequest;
import com.lagou.rpc.common.RpcResponse;
import com.lagou.rpc.provider.anno.RpcService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ChannelHandler.Sharable
public class RpcServerHandler extends SimpleChannelInboundHandler<String> implements ApplicationContextAware {


    private static final Map SERVICE_INSTANCE_MAP = new ConcurrentHashMap();


    /**
     * 将标有@RpcService注解的类缓存起来
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if(serviceMap.size() > 0){
            Set<Map.Entry<String, Object>> entries = serviceMap.entrySet();
            for (Map.Entry<String, Object> item: entries) {
                Object serviceBean = item.getValue();
                if(serviceBean.getClass().getInterfaces().length == 0){
                    throw new RuntimeException("服务必须实现接口");
                }

                // 默认取第一个接口作为缓存bean的名称
                String name = serviceBean.getClass().getInterfaces()[0].getName();
                SERVICE_INSTANCE_MAP.put(name, serviceBean);
            }
        }

        System.out.println(SERVICE_INSTANCE_MAP.toString());
    }

    /**
     * 通道读取就绪事件
     * @param channelHandlerContext
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {

        String host = ((InetSocketAddress)channelHandlerContext.channel().localAddress()).getAddress().getHostAddress();
        int port = ((InetSocketAddress)channelHandlerContext.channel().localAddress()).getPort();
        System.out.println("-----服务器【"+host+":"+port+"】接收到请求-----");

        //1.接收客服端请求，将客户端传过来的消息转换成RpcRequest
        RpcRequest rpcRequest = JSON.parseObject(s, RpcRequest.class);
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(rpcRequest.getRequestId());
        try{
            //业务处理
            Object result = handler(rpcRequest);

            //设置业务处理成功结果
            rpcResponse.setResult(result);
        }catch (Exception e){
            e.printStackTrace();

            //设置业务处理失败结果
            rpcResponse.setError(e.getMessage());
        }

        //6. 给客户端响应
        channelHandlerContext.writeAndFlush(JSON.toJSONString(rpcResponse));



    }

    public Object handler(RpcRequest rpcRequest) throws InvocationTargetException {

        //3.根据传递过来的beanName 从缓存中查找到对应的Bean
        Object serviceBean = SERVICE_INSTANCE_MAP.get(rpcRequest.getClassName());

        if(serviceBean == null){
            throw new RuntimeException("找不到服务");
        }

        //4.解析请求中的方法，参数类型，参数信息
        Class<?> serviceBeanClass = serviceBean.getClass();
        String methodName = rpcRequest.getMethodName();
        Class<?>[] parameterTypes = rpcRequest.getParameterTypes();
        Object[] parameters = rpcRequest.getParameters();

        //利用反射调用bean的方法
        FastClass fastClass = FastClass.create(serviceBeanClass);
        FastMethod fastMethod = fastClass.getMethod(methodName,parameterTypes);
        return fastMethod.invoke(serviceBean,parameters);




    }


}
